package cn.phub.basic.service.impl;

import cn.phub.basic.constant.BaseConstant;
import cn.phub.basic.dto.LoginDto;
import cn.phub.basic.exception.MyException;
import cn.phub.basic.jwt.JwtUtils;
import cn.phub.basic.jwt.RsaUtils;
import cn.phub.basic.jwt.UserInfo;
import cn.phub.basic.service.ILoginService;
import cn.phub.basic.util.AjaxResult;
import cn.phub.basic.util.HttpClientUtils;
import cn.phub.basic.util.MD5Utils;
import cn.phub.basic.util.StrUtils;
import cn.phub.system.domain.Menu;
import cn.phub.system.mapper.MenuMapper;
import cn.phub.system.mapper.PermissionMapper;
import cn.phub.user.domain.Logininfo;
import cn.phub.user.domain.User;
import cn.phub.user.domain.Wxuser;
import cn.phub.user.mapper.LogininfoMapper;
import cn.phub.user.mapper.UserMapper;
import cn.phub.user.mapper.WxuserMapper;
import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.StringUtils;

import java.security.PrivateKey;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


@Service
public class LoginServiceImpl implements ILoginService {
    @Autowired
    private LogininfoMapper logininfoMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private WxuserMapper wxuserMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private MenuMapper menuMapper;
    @Autowired
    private PermissionMapper permissionMapper;

    @Value("${jwt.rsa.pri}")
    private String jwtRsaPrivate;//私钥的文件名字

    @Override
    public Map<String, Object> account(LoginDto dto) {
        //校验参数
        if(StringUtils.isEmpty(dto.getUsername())
            || StringUtils.isEmpty(dto.getPassword())
            || StringUtils.isEmpty(dto.getType())){
            throw new MyException("你妈的参数不能为空！");
        }
        //查询logininfo表 匹配username，phone，email
        Logininfo logininfo = logininfoMapper.loadByUsernameAndType(dto.getUsername(),dto.getType());
        //查询没值就报错
        if(logininfo == null){
            throw new MyException("用户不存在,请重新输入!");
        }
        //查询有值然后判断密码并比对密码 密码不一致就报错
        String salt = logininfo.getSalt();
        String md5pwd = MD5Utils.encrypByMd5(dto.getPassword() + salt);
        if(!md5pwd.equals(logininfo.getPassword())){
            throw new MyException("用户名或密码错了!");
        }
        //登录成功之后的业务处理
        return loginSuccessHandler(logininfo);

//        //当登录信息保存到redis时 设置过期时间30分钟
//        String token = UUID.randomUUID().toString();
//        redisTemplate.opsForValue().set(
//                token,
//                logininfo,//这里是对象存储到内存
//                30,
//                TimeUnit.MINUTES
//        );
//        //返回token和logininfo给前端
//        Map<String, Object> map = new HashMap<>();
//        map.put("token",token);
//        //将一些敏感信息不返回给前端  比如：salt和password
//        logininfo.setSalt(null);
//        logininfo.setPassword(null);
//        map.put("logininfo",logininfo);
//        return map;
    }

    /**
     * 登陆之后的业务处理
     * @param logininfo
     * @return
     */
    private Map<String, Object> loginSuccessHandler(Logininfo logininfo) {
        //登录分为前端用户登录和员工登录
        UserInfo userInfo = new UserInfo();
        //当前登陆人
        userInfo.setUser(logininfo);
        //前端
        if(logininfo.getType() == 1){
            //用户登录是不需要查询权限的
            userInfo.setMenus(null);
            userInfo.setPermissions(null);
        }else{//后端
            //员工登录才需要查询权限，查询出当前登录人所拥有的菜单权限
            List<Menu> Menus = menuMapper.loadMenuByLoginInfo(logininfo.getId());
            //拥有的按钮权限
            List<String> permission = permissionMapper.loadPermissionByLoginIn(logininfo.getId());
            userInfo.setMenus(Menus);
            userInfo.setPermissions(permission);
        }
        //使用JWT工具类 将登录信息和权限信息 封装成JWT加密串
        try {
            //获取私钥
            PrivateKey privateKey = RsaUtils
                    .getPrivateKey(FileCopyUtils
                            .copyToByteArray(this.getClass().getClassLoader().getResourceAsStream(jwtRsaPrivate)));
            //JWT串
            String token = JwtUtils.generateTokenExpireInMinutes(userInfo, privateKey, 24*60);
            //将加密之后的串返回给前端
            Map<String, Object> map = new HashMap<>();
            map.put("token",token);//JWT串--当前登陆人的信息和菜单信息和按钮信息
            //前端没有写JWT解密实现，直接将信息还是原样放回给前端
            logininfo.setSalt(null);
            logininfo.setPassword(null);
            map.put("logininfo",logininfo);
            map.put("menus",userInfo.getMenus());
            map.put("permissions",userInfo.getPermissions());
            return map;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 微信登录接口
     * @param param
     * @return
     */
    @Override
    public AjaxResult wechat(Map<String, String> param) {
        //校验参数
        String code = param.get("code");
        if (StringUtils.isEmpty(code)) {
            throw new MyException("授权码空的!");
        }
        //根据code,拼接从微信获取的token请求
        String tokenUrl = BaseConstant.WechatConstant.TOKEN_URL
                .replace("APPID",BaseConstant.WechatConstant.APPID)
                .replace("SECRET",BaseConstant.WechatConstant.SECRET)
                .replace("CODE",code);
        //向微信发起获取token请求,接收微信的响应
        String httpToken = HttpClientUtils.httpGet(tokenUrl);
        JSONObject jsonObject = JSONObject.parseObject(httpToken);
        String accessToken = jsonObject.getString("access_token");
        String openId = jsonObject.getString("openid");
        //根据openID查询表u_wxuser 判断此用户是否已经存在
        Wxuser wxuser = wxuserMapper.loadByOpenId(openId);
        if(wxuser != null && wxuser.getUserId() != null){
            //存在 并且已经绑定了 直接免密登陆
            Logininfo logininfo = logininfoMapper.loadByUserId(wxuser.getUserId());
            //登录成功之后的业务处理
            Map<String, Object> map = loginSuccessHandler(logininfo);
            return AjaxResult.me().setResultObj(map);
//            //当登录信息保存到redis 设置过期时间30分钟
//            String token = UUID.randomUUID().toString();
//            redisTemplate.opsForValue().set(
//                    token,
//                    logininfo,//对象存储到内存
//                    30,
//                    TimeUnit.MINUTES
//            );
//            //返回token,logininfo给前端
//            Map<String, Object> map = new HashMap<>();
//            map.put("token",token);
//            //将一些敏感信息不要返回给前端  salt password
//            logininfo.setSalt(null);
//            logininfo.setPassword(null);
//            map.put("logininfo",logininfo);
//            return AjaxResult.me().setResultObj(map);
        }
        //不存在，让页面定位到微信绑定页面
        //需要携带access_token + openid回去
        String binderParmaStr = "?accessToken="+accessToken+"&openId="+openId;
        return AjaxResult.me().setSuccess(false).setResultObj(binderParmaStr);
    }

    @Override
    public Map<String, Object> binder(Map<String, String> param) {
        String phone = param.get("phone");
        String verifyCode = param.get("verifyCode");
        String accessToken = param.get("accessToken");
        String openId = param.get("openId");
        //校验参数
        if(StringUtils.isEmpty(phone)
                || StringUtils.isEmpty(verifyCode)
                || StringUtils.isEmpty(accessToken)
                || StringUtils.isEmpty(openId)
        ){
            throw new MyException("参数不能为空!");
        }
        //校验短信验证码
        String smsCodeKey = BaseConstant.SmsConstant.BUSINESS_BINDER_PREFIX+phone;
        Object smsCodeValueTmp = redisTemplate.opsForValue().get(smsCodeKey);
        if(smsCodeValueTmp == null){
            throw new MyException("验证码已过期,请重新获取!");
        }
        String smsCodeValue = smsCodeValueTmp.toString().split(":")[0];
        if(!smsCodeValue.equalsIgnoreCase(verifyCode)){
            throw new MyException("验证码错误!");
        }
        //判断用户是否已经存在
        User user = userMapper.loadByPhone(phone);
        //抽取一个公共变量
        Logininfo logininfo = null;
        if(user == null){
            //不存在就生成user和logininfo
            user = initUser(phone);
            logininfo  =user2logininfo(user);
            logininfoMapper.save(logininfo);//返回自增id
            user.setLogininfoId(logininfo.getId());
            userMapper.save(user);
        }else{
            //存在则查询user
            //查询到给下面保存用
            logininfo = logininfoMapper.loadByUserId(user.getId());
        }
        //拼接获取微信微信用户信息的请求，调用微信去获取微信用户基本信息
        String wxUserUrl = BaseConstant.WechatConstant.WXUSER_URL
                .replace("ACCESS_TOKEN",accessToken)
                .replace("OPENID",openId);
        String wxUserJsonStr = HttpClientUtils.httpGet(wxUserUrl);
        //微信响应结果
        Wxuser wxuser = JSONObject.parseObject(wxUserJsonStr, Wxuser.class);
        wxuser.setUserId(user.getId());
        //绑定user，保存t_wxuser
        wxuserMapper.save(wxuser);
        return loginSuccessHandler(logininfo);
//        //免密登录成功并跳到主页,显示用户信息,登录信息保存到redis-设置过期时间30分钟
//        String token = UUID.randomUUID().toString();
//        redisTemplate.opsForValue().set(
//                token,
//                logininfo,//将对象存储到内存
//                30,
//                TimeUnit.MINUTES
//        );
//        //返回token和logininfo给前端
//        Map<String, Object> map = new HashMap<>();
//        map.put("token",token);
//        //将一些敏感信息不要返回给前端salt+password
//        logininfo.setSalt(null);
//        logininfo.setPassword(null);
//        map.put("logininfo",logininfo);
//        return map;

    }

    private Logininfo user2logininfo(User user) {
        Logininfo logininfo = new Logininfo();
        BeanUtils.copyProperties(user,logininfo);
        logininfo.setType(1);//一定要设置
        return logininfo;
    }

    private User initUser(String phone) {
        User user = new User();
        user.setUsername(phone);
        user.setPhone(phone);
        String salt = StrUtils.getComplexRandomString(32);
        //需要发送一个短信告诉phone，需要初始化密码
        String md5Pwd = MD5Utils.encrypByMd5("1" + salt);
        user.setSalt(salt);
        user.setPassword(md5Pwd);
        return user;
    }
}
